import { User } from 'discord.js';
import UserModel, { defaultUserPermissions } from './models/user';
import PermissionGroupModel from './models/permGroup';
import { ExtractQuery } from '../types';

// Authentication Utility; handles user authentication, for bot-wide perms, etc...
/** Get a user's db entry */
export const getUserEntry = async (
  user: User | string,
  createIfNonexistent = false,
) => {
  let uname: string | undefined = undefined;
  if (typeof user !== 'string') {
    uname = user.username;
    user = user.id;
  }
  let res = await UserModel.findOne({
    userId: user,
  });
  if (!res && createIfNonexistent) {
    res = new UserModel();
    res.userId = user;
  }
  if (res && uname && res?.userName !== uname) {
    res.userName = uname;
  }
  return res;
};

/** Resolves groups */
export const getGroups = (groupIds: string[]) =>
  Promise.all(
    groupIds.map(v =>
      PermissionGroupModel.findOne({
        groupName: v,
      }),
    ),
  );

/** Checks if a user has a permission */
export const getUserGroups = async (
  user: User | string,
  createIfNonexistent = false,
) => {
  const userEntry = await getUserEntry(user, createIfNonexistent);
  return await getGroups(userEntry?.groups ?? []);
};

/** Checks if a user has a permission */
export const getUserPermissionsFromEntry = async (
  userEntry: ExtractQuery<ReturnType<typeof UserModel.findOne>>,
) => {
  const groups = await getGroups(userEntry?.groups ?? []);
  const perms = {
    ...defaultUserPermissions,
  };
  const applyPermissions = (applyPerms: Partial<typeof perms>) => {
    const vals = Object.entries(applyPerms).filter(([_, b]) => b);
    vals.forEach(v => (perms[v[0] as keyof typeof perms] = v[1]));
  };
  groups.forEach(group => applyPermissions(group?.permissions ?? {}));
  applyPermissions(userEntry?.permissions ?? {});
  return perms;
};

/** Checks if a user has a permission */
export const getUserPermissions = async (
  user: User | string,
  createIfNonexistent = false,
) => {
  const u = await getUserEntry(user, createIfNonexistent);
  if (!u) return defaultUserPermissions;
  return await getUserPermissionsFromEntry(u);
};
